#include "raft_stuff.h"
#include "in_memory_state_mgr.h"
#include "log_state_machine.h"
#include "logger.h"
#include "raft_logger_wrapper.h"

RaftStuff::RaftStuff(int node_id,const std::string& endpoint,int port,VectorDatabase* vector_database):
    node_id(node_id),endpoint(endpoint),port_(port),vector_database(vector_database)
{
    init();
}
void RaftStuff::init(){
    // 相关对象初始化
    smgr_=cs_new<inmem_state_mgr>(node_id,endpoint,vector_database);
    sm_=cs_new<log_state_machine>();

    ptr<log_state_machine> log_sm=std::dynamic_pointer_cast<log_state_machine>(sm_);
    log_sm->setVectorDatabase(vector_database);

    // 设置asio服务选项
    asio_service::options asio_opt;
    asio_opt.thread_pool_size_=1;

    //设置raft参数
    raft_params params;
    params.election_timeout_lower_bound_=10000;
    params.election_timeout_upper_bound_=20000;

    // Logger
    std::string log_file_name="./srv.log";
    ptr<logger_wrapper> log_war=cs_new<logger_wrapper>(log_file_name);
    
    //初始化raft实例
    raft_instance_=launcher_.init(sm_,smgr_,nullptr,port_,asio_opt,params);
    GlobalLogger->debug("RaftStuff init with node_id:{} endpoint:{} port:{}",node_id,endpoint,port_);
}

ptr<cmd_result<ptr<buffer>>> RaftStuff::addSrv(int srv_id, const std::string &endpoint)
{
    ptr<srv_config> peer_srv_conf=cs_new<srv_config>(srv_id,endpoint);
    GlobalLogger->debug("addSrv srv_id:{} endpoint:{}",srv_id,endpoint);
    return raft_instance_->add_srv(*peer_srv_conf);
}

void RaftStuff::enableElectionTimeout(int lower_bound, int upper_bound)
{
    if(raft_instance_){
        raft_params params=raft_instance_->get_current_params();
        params.election_timeout_lower_bound_=lower_bound;
        params.election_timeout_upper_bound_=upper_bound;
        raft_instance_->update_params(params);
    }
}

std::vector<std::tuple<int, std::string, std::string, nuraft::ulong, nuraft::ulong>> RaftStuff::getAllNodesInfo() const
{
    std::vector<std::tuple<int, std::string, std::string, nuraft::ulong, nuraft::ulong>> nodes_info;
    if(!raft_instance_){
        return nodes_info;
    }

    auto conf=raft_instance_->get_config();
    if(!conf){
        return nodes_info;
    }

    auto servers=conf->get_servers();
    for(auto& srv:servers){
        if(srv){
            std::string node_state;
            if(srv->get_id()==raft_instance_->get_leader()){
                node_state="leader";
            }else{
                node_state="follower";
            }

            nuraft::raft_server::peer_info node_info=raft_instance_->get_peer_info(srv->get_id());
            nuraft::ulong last_log_index=node_info.last_log_idx_;
            nuraft::ulong last_succ_resp_us=node_info.last_succ_resp_us_;
            nodes_info.push_back(std::make_tuple(srv->get_id(),srv->get_endpoint(),node_state,last_log_index,last_succ_resp_us));
        }
    }

    return nodes_info;
}

std::tuple<int, std::string, std::string, nuraft::ulong, nuraft::ulong> RaftStuff::getCurrentNodesInfo() const
{
    std::tuple<int, std::string, std::string, nuraft::ulong, nuraft::ulong> nodes_info;
    if(!raft_instance_){
        return nodes_info;
    }

    auto conf=raft_instance_->get_config();
    if(!conf){
        return nodes_info;
    }

    auto servers=conf->get_servers();
    for(auto& srv:servers){
        if(srv&&srv->get_id()==node_id){
            std::string node_state;
            if(srv->get_id()==raft_instance_->get_leader()){
                node_state="leader";
            }else{
                node_state="follower";
            }

            nuraft::raft_server::peer_info node_info=raft_instance_->get_peer_info(srv->get_id());
            nuraft::ulong last_log_index=node_info.last_log_idx_;
            nuraft::ulong last_succ_resp_us=node_info.last_succ_resp_us_;
            nodes_info=std::make_tuple(srv->get_id(),srv->get_endpoint(),node_state,last_log_index,last_succ_resp_us);

            break;
        }
    }
    return nodes_info;
}

std::string RaftStuff::getNodeStatus(int node_id) const
{
    return std::string();
}

ptr<cmd_result<ptr<buffer>>> RaftStuff::appendEntries(const std::string &entry)
{
    if(!raft_instance_||!raft_instance_->is_leader()){
        if(!raft_instance_){
            GlobalLogger->debug("Cannot append entries: Raft instance is not available");
        }else{
            GlobalLogger->debug("Cannot append entries: Raft instance is not leader");
        }
        return nullptr;
    }

    size_t total_size=sizeof(int)+entry.size();

    GlobalLogger->debug("Append entries: size:{}",total_size);

    ptr<buffer> entry_buf=buffer::alloc(total_size);
    buffer_serializer bs(entry_buf);
    bs.put_str(entry);

    GlobalLogger->debug("Created log_entry_buffer at address: {}", static_cast<const void*>(entry_buf.get()));
    GlobalLogger->debug("Append entries to raft instance");
    return raft_instance_->append_entries({entry_buf});
}

bool RaftStuff::is_leader()const{
    if(!raft_instance_){
        return false;
    }
    return raft_instance_->is_leader();
}